
#include "graphics/particleemitter.h"
#include "graphics/texturemanager.h"
#include "graphics/renderer.h"

namespace dw
{
	namespace graphics
	{

		int clamped_random(int min_val, int max_val)
		{
			return rand() % (max_val - min_val) + min_val;
		}

		Particle::Particle() :
			ttl(-1.0f),
			mass(0.0f),
			forceSum(0.0f, -9.8f, 0.0f)
		{
		}

		bool Particle::update(const float dT)
		{
			if ((ttl -= dT) <= 0.0f)
				return false;
			Vector3f a = forceSum * mass;
			velocity = velocity + a * dT;
			position = position + velocity * dT;
			return true;
		}

		IParticleEmitter::IParticleEmitter(const Point3f & position, const uint32 particleCount, const std::string & name) :
			_position(position),
			_particleCount(particleCount),
			_name(name)
		{
			for (uint32 i = 0; i < particleCount; ++i)
				_particles.push_back(Particle());
		}

		IParticleEmitter::~IParticleEmitter()
		{
			_particles.clear();
		}

		void IParticleEmitter::setParticleCount(const uint32 particleCount)
		{
			while (_particles.size() > particleCount)
				_particles.push_back(Particle());
			while (particleCount < _particles.size())
				_particles.pop_back();
			_particleCount = particleCount;
		}

		Spray::Spray(const Point3f & position, const Vector3f & direction, const uint32 particleCount, const std::string & name) :
			IParticleEmitter(position, particleCount, name),
			_direction(direction)
		{
			setVelocity(1.0f, 0.1f);
			setTTL(20.0f, 0.1f);

			Vector2f v;
			v[0] = v[1] = 3.14f;

			setDeltaAngle(v);
			setDeltaPosition(Vector3f::zero);

			_rof = _aTTL / (float)_particleCount;
			_last = 0.0f;
		}

		Spray::Spray(const std::string & name) :
			IParticleEmitter(Point3f(0.0, 0.0, 0.0), 10, name),
			_direction(Vector3f(0.0, 1.0, 0.0))
		{
			setVelocity(1.0f, 0.1f);
			setTTL(20.0f, 0.1f);

			Vector2f v;
			v[0] = v[1] = 3.14f;

			setDeltaAngle(v);
			setDeltaPosition(Vector3f::zero);

			_rof = _aTTL / (float)_particleCount;
			_last = 0.0f;
		}

		void Spray::setVelocity(float averageVelocity, float delta)
		{
			_aVelocity = averageVelocity;
			_dVelocity = delta;
		}

		void Spray::setTTL(float averageTTL, float delta)
		{
			_aTTL = averageTTL;
			_dTTL = delta;
		}
		void Spray::setMass(float averageMass, float delta)
		{
			_aMass = averageMass;
			_dMass = delta;
		}

		void Spray::setDeltaAngle(const Vector2f deltaAngle)
		{
			_dAngle = deltaAngle;
		}

		void Spray::setDeltaPosition(const Vector3f deltaPosition)
		{
			_dPosition = deltaPosition;
		}

		void Spray::reset(Particle & particle)
		{
			Vector3f angle;
			Point3f position;

			angle[0] = (float)clamped_random(-1000, 1000) / 1000.0f * _dAngle[0];
			angle[1] = (float)clamped_random(-1000, 1000) / 1000.0f * _dAngle[0];
			
			position[0] = (float)clamped_random(-1000, 1000) / 1000.0f * _dPosition[0];
			position[1] = (float)clamped_random(-1000, 1000) / 1000.0f * _dPosition[1];
			position[2] = (float)clamped_random(-1000, 1000) / 1000.0f * _dPosition[2];
			particle.position = /*_position + */position;
			
			particle.velocity = _direction;
			particle.velocity = Matrix33f::rotation(Vector3f(1, 0, 0), angle[0]) * particle.velocity;								// Rotation du vecteur de la particule par la matrice de rotation
			particle.velocity = Matrix33f::rotation(Vector3f(0, 0, 1), angle[1]) * particle.velocity;

			particle.velocity = particle.velocity * (_aVelocity + ((float)clamped_random(-1000, 1000) / 1000.0f * _dVelocity));
			particle.mass = _aMass * (1 + (float)clamped_random(-1000, 1000) / 1000.0f * _dMass);
			particle.ttl = _aTTL * (1 + (float)clamped_random(-1000, 1000) / 1000.0f * _dTTL);
		}

		void Spray::update(const float dT)
		{
			float emit(-1.0f);

			_last += dT;
			for (uint32 it = 0; it < _particleCount; ++it)
			{
				if (!_particles[it].update(dT))
				{
					if  (_last > _rof)
						emit = _last / _rof - 1.0f;
					while (emit > -1.0f)
					{
						reset(_particles[it]);
						emit -= 1.0f;
						_last = 0.0f;
					}
				}
				else
				{
					if (_boundingBox.width / 2.0f < abs(_particles[it].position.x)
						|| _boundingBox.height / 2.0f < abs(_particles[it].position.y) 
						|| _boundingBox.depth / 2.0f < abs(_particles[it].position.z))
						_particles[it].ttl *= 0.9f;
				}
			}
		}

		void Spray::render(Renderer * renderer) const
		{
			float color = 1.0f;
			Vector3f pos(_position.x, _position.y, _position.z);

			/*glBegin(GL_LINE_LOOP);
			glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
			glVertex3f(_position.x() - 10.0f, _position.y() - 10.0f, _position.z());
			glVertex3f(_position.x() - 10.0f, _position.y() + 10.0f, _position.z());
			glVertex3f(_position.x() + 10.0f, _position.y() + 10.0f, _position.z());
			glVertex3f(_position.x() + 10.0f, _position.y() - 10.0f, _position.z());
			glEnd();*/

			renderer->pushMatrix();
			renderer->multMatrix(Matrix44f::translation(pos));
			
			renderer->drawBox(_boundingBox);

			glEnable(GL_POINT_SPRITE_ARB);
			renderer->setTexture(TextureManager::getInstance().loadTexture("smoke.dds"));
			glPointSize(10);

			glBegin(GL_POINTS);
			glPointSize(10.0f);
			for (uint32 it = 0; it < _particleCount; ++it)
			{
				if (_particles[it].ttl > 0.0)
				{
					glColor4f(0.7f, 0.8f, 1.0f, _particles[it].ttl / _aTTL);
					glVertex3f(_particles[it].position.x, _particles[it].position.y, _particles[it].position.z);
				}
			}
			glEnd();
			
			glDisable(GL_POINT_SPRITE_ARB);
			renderer->popMatrix();
		}

	} // namespace graphics
} // namespace dw
